/*
  Losu Copyright Notice
  --------------------
    Losu is an open source programming language project under the MIT license
  that can be used for both academic and commercial purposes. There are no
  fees, no royalties, and no GNU-like "copyright" restrictions. Losu qualifies
  as open source software. However, Losu is not public property, and founder
  'chen-chaochen' retains its copyright.

    Losu has been registered with the National Copyright Administration of the
  People's Republic of China, and adopts the MIT license as the copyright
  licensing contract under which the right holder conditionally licenses its
  reproduction, distribution, and modification rights to an unspecified public.

    If you use Losu, please follow the public MIT agreement or choose to enter
  into a dedicated license agreement with us.

  The MIT LICENSE is as follows
  --------------------
  Copyright  2020 - now  chen-chaochen

    Permission is hereby granted, free of charge, to any person obtaining a
  copy of this software and associated documentation files (the “Software”), to
  deal in the Software without restriction, including without limitation the
  rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
  sell copies of the Software, and to permit persons to whom the Software is
  furnished to do so, subject to the following conditions:

    The above copyright notice and this permission notice shall be included in
  all copies or substantial portions of the Software.

    THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
  AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
  LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
  OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
  SOFTWARE.
*/

#include "math.lsh.h"
#include <math.h>

#define PI 3.141592653589793
#define E 2.718281828459045
#define GAMMA 0.577215664901532

/**
 * @fn abs(x:float) -> float
 * @brief 返回 x 的绝对值
 */
int32_t
__losuh__func_abs_base_math (LosuVm *vm)
{
  arg_returnnum (vm, (_l_number)fabs (arg_getnum (vm, 1)));
  return 1;
}
/**
 * @fn mod(a:float, b:float) -> float
 * @brief 返回 a 除以 b 的余数
 */
int32_t
__losuh__func_mod_base_math (LosuVm *vm)
{
  arg_returnnum (vm, (_l_number)fmod (arg_getnum (vm, 1), arg_getnum (vm, 2)));
  return 1;
}
/**
 * @fn pow(x:float, y:float) -> float
 * @brief 返回 x 的 y 次幂
 */
int32_t
__losuh__func_pow_base_math (LosuVm *vm)
{
  arg_returnnum (vm, (_l_number)pow (arg_getnum (vm, 1), arg_getnum (vm, 2)));
  return 1;
}
/**
 * @fn sqrt(x:float) -> float
 * @brief 返回 x 的平方根
 */
int32_t
__losuh__func_sqrt_base_math (LosuVm *vm)
{
  arg_returnnum (vm, (_l_number)sqrt (arg_getnum (vm, 1)));
  return 1;
}
/**
 * @fn anyroot(x:float, n:float) -> float
 * @brief 返回 x 的 n 次方根（近似根）
 */
int32_t
__losuh__func_anyroot_base_math (LosuVm *vm)
{
  double x, n;
  x = arg_getnum (vm, 1);
  n = arg_getnum (vm, 2);
  arg_returnnum (vm, exp (1 / n) * log (x));
  return 1;
}
/**
 * @fn gcd(a:int, b:int) -> int
 * @brief 返回 a 和 b 的最大公约数
 */
int32_t
__losuh__func_gcd_base_math (LosuVm *vm)
{
  int64_t a, b, mid = 0;
  a = arg_getnum (vm, 1);
  b = arg_getnum (vm, 2);
  while (b != 0)
    {
      mid = a;
      a = b;
      b = mid % b;
    }
  arg_returnnum (vm, a);
  return 1;
}
/**
 * @fn acos(x:float) -> float
 * @brief 返回 x 的反余弦值
 */
int32_t
__losuh__func_acos_trigonometric_math (LosuVm *vm)
{
  arg_returnnum (vm, (_l_number)acos (arg_getnum (vm, 1)));
  return 1;
}
/**
 * @fn asin(x:float) -> float
 * @brief 返回 x 的反正弦值
 */
int32_t
__losuh__func_asin_trigonometric_math (LosuVm *vm)
{
  arg_returnnum (vm, (_l_number)asin (arg_getnum (vm, 1)));
  return 1;
}
/**
 * @fn atan(x:float) -> float
 * @brief 返回 x 的反正切值
 */
int32_t
__losuh__func_atan_trigonometric_math (LosuVm *vm)
{
  arg_returnnum (vm, (_l_number)atan (arg_getnum (vm, 1)));
  return 1;
}
/**
 * @fn cos(x:float) -> float
 * @brief 返回 x 的余弦值
 */
int32_t
__losuh__func_cos_trigonometric_math (LosuVm *vm)
{
  arg_returnnum (vm, (_l_number)cos (arg_getnum (vm, 1)));
  return 1;
}
/**
 * @fn sin(x:float) -> float
 * @brief 返回 x 的正弦值
 */
int32_t
__losuh__func_sin_trigonometric_math (LosuVm *vm)
{
  arg_returnnum (vm, (_l_number)sin (arg_getnum (vm, 1)));
  return 1;
}
/**
 * @fn tan(x:float) -> float
 * @brief 返回 x 的正切值
 */
int32_t
__losuh__func_tan_trigonometric_math (LosuVm *vm)
{
  arg_returnnum (vm, (_l_number)tan (arg_getnum (vm, 1)));
  return 1;
}
/**
 * @fn log(x:float, base:float) -> float
 * @brief 返回 x 的以 base 为底的对数
 */
int32_t
__losuh__func_log_logarithmic_math (LosuVm *vm)
{
  arg_returnnum (vm, (_l_number)log (arg_getnum (vm, 1))
                         / log (arg_getnum (vm, 2)));
  return 1;
}
/**
 * @fn ln(x:float) -> float
 * @brief 返回 x 的自然对数
 */
int32_t
__losuh__func_ln_logarithmic_math (LosuVm *vm)
{
  arg_returnnum (vm, (_l_number)log (arg_getnum (vm, 1)));
  return 1;
}
/**
 * @fn log10(x:float) -> float
 * @brief 返回 x 的以 10 为底的对数
 */
int32_t
__losuh__func_log10_logarithmic_math (LosuVm *vm)
{
  arg_returnnum (vm, (_l_number)log10 (arg_getnum (vm, 1)));
  return 1;
}
/**
 * @fn exp(x:float) -> float
 * @brief 返回 e 的 x 次幂
 */
int32_t
__losuh__func_exp_logarithmic_math (LosuVm *vm)
{
  arg_returnnum (vm, (_l_number)exp (arg_getnum (vm, 1)));
  return 1;
}
/**
 * @fn ceil(x:float) -> float
 * @brief 返回大于或等于 x 的最小整数
 */
int32_t
__losuh__func_ceil_rounding_math (LosuVm *vm)
{
  arg_returnnum (vm, (_l_number)ceil (arg_getnum (vm, 1)));
  return 1;
}
/**
 * @fn floor(x:float) -> float
 * @brief 返回小于或等于 x 的最大整数
 */
int32_t
__losuh__func_floor_rounding_math (LosuVm *vm)
{
  arg_returnnum (vm, (_l_number)floor (arg_getnum (vm, 1)));
  return 1;
}
/**
 * @fn round(x:float) -> float
 * @brief 返回最接近 x 的整数（四舍五入）
 */
int32_t
__losuh__func_round_rounding_math (LosuVm *vm)
{
  arg_returnnum (vm, (_l_number)round (arg_getnum (vm, 1)));
  return 1;
}
/**
 * @fn cut(x:float, n:int) -> float
 * @brief 截断 x ，保留 n 位小数
 */
int32_t
__losuh__func_cut_rounding_math (LosuVm *vm)
{
  _l_number num = arg_getnum (vm, 1);
  int64_t x = arg_getnum (vm, 2);
  x = pow (10, x);
  arg_returnnum (vm, (_l_number)((int64_t)(num * x)) / x);
  return 1;
}
/**
 * @fn movl(x:int, y:int) -> int
 * @brief 返回将 x 左移 y 位的结果
 */
int32_t
__losuh__func_movl_bitwise_math (LosuVm *vm)
{
  uint32_t x = arg_getnum (vm, 1);
  uint32_t y = arg_getnum (vm, 2);
  uint32_t z = x << y;
  arg_returnnum (vm, (_l_number)z);
  return 1;
}
/**
 * @fn movr(x:int, y:int) -> int
 * @brief 返回将 x 右移 y 位的结果
 */
int32_t
__losuh__func_movr_bitwise_math (LosuVm *vm)
{
  uint32_t x = arg_getnum (vm, 1);
  uint32_t y = arg_getnum (vm, 2);
  uint32_t z = x >> y;
  arg_returnnum (vm, (_l_number)z);
  return 1;
}
/**
 * @fn bitand(x:int, y:int) -> int
 * @brief 返回 x 和 y 的按位与结果
 */
int32_t
__losuh__func_bitand_bitwise_math (LosuVm *vm)
{
  uint32_t x = arg_getnum (vm, 1);
  uint32_t y = arg_getnum (vm, 2);
  uint32_t z = x & y;
  arg_returnnum (vm, (_l_number)z);
  return 1;
}
/**
 * @fn bitor(x:int, y:int) -> int
 * @brief 返回 x 和 y 的按位或结果
 */
int32_t
__losuh__func_bitor_bitwise_math (LosuVm *vm)
{
  uint32_t x = arg_getnum (vm, 1);
  uint32_t y = arg_getnum (vm, 2);
  uint32_t z = x | y;
  arg_returnnum (vm, (_l_number)z);
  return 1;
}
/**
 * @fn bitxor(x:int, y:int) -> int
 * @brief 返回 x 和 y 的按位异或结果
 */
int32_t
__losuh__func_bitxor_bitwise_math (LosuVm *vm)
{
  uint32_t x = arg_getnum (vm, 1);
  uint32_t y = arg_getnum (vm, 2);
  uint32_t z = x ^ y;
  arg_returnnum (vm, (_l_number)z);
  return 1;
}
/**
 * @fn bitnot(x:int) -> int
 * @brief 返回 x 的按位取反结果
 */
int32_t
__losuh__func_bitnot_bitwise_math (LosuVm *vm)
{
  uint32_t x = arg_getnum (vm, 1);
  uint32_t z = ~x;
  arg_returnnum (vm, (_l_number)z);
  return 1;
}
/**
 * @fn sum(a:list) -> float
 * @brief 返回数组 a 的元素之和
 */
int32_t
__losuh__func_sum_array_math (LosuVm *vm)
{
  LosuObj *list = arg_get (vm, 1);
  _l_number sum = 0;
  LosuObj *val = NULL;
  int32_t n = 1;
  while (1)
    {
      val = obj_indexunitbynum (vm, *list, n);
      if (obj_type (vm, val) == LosuTypeDefine_null)
        break;
      sum += obj_tonum (vm, val);
      n++;
    }
  arg_returnnum (vm, sum);
  return 1;
}
/**
 * @fn prod(a:list) -> float
 * @brief 返回数组 a 的元素之积
 */
int32_t
__losuh__func_prod_array_math (LosuVm *vm)
{
  LosuObj *list = arg_get (vm, 1);
  _l_number prod = 1;
  LosuObj *val = NULL;
  int32_t n = 1;
  while (1)
    {
      val = obj_indexunitbynum (vm, *list, n);
      if (obj_type (vm, val) == LosuTypeDefine_null)
        break;
      prod *= obj_tonum (vm, val);
      n++;
    }
  arg_returnnum (vm, prod);
  return 1;
}
/**
 * @fn mean(a:list) -> float
 * @brief 返回数组 a 的元素的平均值
 */
int32_t
__losuh__func_mean_array_math (LosuVm *vm)
{
  LosuObj *unit = arg_get (vm, 1);
  LosuObj *val;
  _l_number output = 0;
  unsigned int index = 0;
  while (1)
    {
      val = obj_indexunitbynum (vm, *unit, index + 1);
      if (obj_type (vm, val) == LosuTypeDefine_null)
        break;
      output += obj_tonum (vm, val);
      index++;
    }

  if (index == 0)
    arg_returnnum (vm, 0);
  else
    arg_returnnum (vm, output / index);
  return 1;
}
/**
 * @fn rad2deg(x:float) -> float
 * @brief 将弧度转换为角度
 */
int32_t
__losuh__func_rad2deg_angle_math (LosuVm *vm)
{
  double x = arg_getnum (vm, 1);
  double z = x * 180 / PI;
  arg_returnnum (vm, (_l_number)z);
  return 1;
}
/**
 * @fn deg2rad(x:float) -> float
 * @brief 将角度转换为弧度
 */
int32_t
__losuh__func_deg2rad_angle_math (LosuVm *vm)
{
  double x = arg_getnum (vm, 1);
  double z = x * PI / 180;
  arg_returnnum (vm, (_l_number)z);
  return 1;
}
